import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: create by Young
 * @version: v1.0
 * @description: PACKAGE_NAME
 * @date:2019/4/9 3:06
 */

//有界队列：队列空时，读线程将阻塞，队列满时，写线程将阻塞
class BoundedQueue<T> {
	private Object[] items;
	private int addIndex, removeIndex, count;
	private Lock lock = new ReentrantLock();
	private Condition notEmpty = lock.newCondition();
	private Condition notFull = lock.newCondition();
	public BoundedQueue(int size) {
		items = new Object[size];
	}
	//如果队列满，则添加线程进入阻塞，直到非满
	public void add(T t) throws InterruptedException {
		lock.lock();
		try {
			System.out.println("adding "+ t + " to the queue");
			while(count == items.length) {
				System.out.println("full queue, wait");
				notFull.await(); //等待notFull.signal()
			}
			items[addIndex] = t;
			if(++addIndex == items.length) {
				addIndex = 0;
			}
			++count;
			notEmpty.signal(); //唤醒 notEmpty.await();
		} finally {
			lock.unlock();
		}
	}

	//从头部删除一个元素，如果队列空，则删除线程阻塞，直到队列非空
	public T remove() throws InterruptedException {
		lock.lock();
		try {
			System.out.println("removing the head node");
			while( count == 0) {
				System.out.println("empty queue, wait");
				notEmpty.await(); //等待 notEmpty.signal()
			}
			Object x = items[removeIndex];
			if(++removeIndex == items.length) {
				removeIndex = 0;
			}
			--count;
			notFull.signal(); //唤醒notFull.await()
			return (T)x;
		} finally {
			lock.unlock();
		}
	}

}

class Add implements Runnable {

	private BoundedQueue<Integer> bq;

	public Add(BoundedQueue<Integer> boundedQueue) {
		bq = boundedQueue;
	}

	@Override
	public void run() {
		for(int i=0; i<10; i++) {
			try {
				bq.add(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class Remove implements Runnable {
	private BoundedQueue<Integer> bq;
	public Remove(BoundedQueue<Integer> boundedQueue) {
		bq = boundedQueue;
	}
	@Override
	public void run() {
		for(int i=0; i<10; i++) {
			try {
				bq.remove();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class Application {

	public static void main(String[] args) {
		BoundedQueue bq = new BoundedQueue(3);
		Add add = new Add(bq);
		Remove remove = new Remove(bq);
		new Thread(add).start();
		new Thread(remove).start();
	}
}
